const { Controller } = require('egg');
const xlsx = require('xlsx-stream');
const moment = require('moment');
const { centToYuan } = require('../../lib/money');
const fs = require('fs');
const Excel = require('exceljs')
const request = require('request');
const compressing = require('compressing');

/* 大部分excel导出接口是流式生成的，因此需要在循环中请求数据 */
/* eslint-disable no-await-in-loop */

function writeAndDrain(stream, data) {
    if (!stream.write(data)) {
        return new Promise((fulfill) => {
            stream.once('drain', fulfill);
        });
    }
    return null;
}

function deleteall(path) {
    var files = [];
    if (fs.existsSync(path)) {
        files = fs.readdirSync(path);
        files.forEach(function (file, index) {
            var curPath = path + "/" + file;
            if (fs.statSync(curPath).isDirectory()) {
                deleteall(curPath);
            } else {
                fs.unlinkSync(curPath);
            }
        });
        fs.rmdirSync(path);
    }
};


async function writeFile(imgPath, writeStream) {
    const readStream = request(imgPath).pipe(writeStream);
    return new Promise((fulfill) => {
        readStream.on("finish", function () {
            writeStream.end();
            fulfill();
        });
    });
}

module.exports = class ApiController extends Controller {
    async exportTaskIdcard() {
        const header = ['体检卡号', '体检卡状态', '有效期', '适用年龄', '体检套餐', '适用性别', '导入人', '导入时间'];
        const stream = await this.ctx.api.task.requestJSONStreamWithoutBody('GET', '/hd/medicalExaminationCards', { limit: 1000, skip: 0, orderByType: 1 });
        const output = xlsx();
        output.write(header);
        stream.on('data', (data) => {
            if (!output.write(data)) {
                stream.pause();
                output.once('drain', () => stream.resume());
            }
        });
        stream.on('end', () => {
            output.finalize();
            output.end();
        });
        stream.resume();
        const filename = '体检卡列表.xlsx';
        this.ctx.response.attachment(filename);
        this.ctx.body = {
            code: 0,
            data: null
        };
    }

    async exportTaskTable() {
        this.logger.info('exportTaskTable', this.ctx.query);
        const header = ['任务id', '主题', '会员', '年龄', '性别', '会员id', '联系方式', '其他联系方式', '门慢卡', '签约医生', '签约机构', '居住地址', '现有疾病', '类型（一级）', '类型（二级）', '来源', '负责人', '任务状态', '通话情况', '处理结果', '其他操作', '备注', '任务创建时间', '记录更新时间'];
        const stream = await this.ctx.api.task.requestJSONStreamWithoutBody('GET', 'crm/task/exportData', this.ctx.query);
        const output = xlsx();
        output.write(header);
        stream.on('data', (data) => {
            if (!output.write(data)) {
                stream.pause();
                output.once('drain', () => stream.resume());
            }
        });
        stream.on('end', () => {
            output.finalize();
            output.end();
        });
        stream.resume();
        const filename = '任务列表.xlsx';
        this.ctx.response.attachment(filename);
        this.ctx.body = output;
    }

    async checkSpareMoneyTableAvailable() {
        const { ctx } = this;
        this.logger.info('exportTaskTable', ctx.query);
        const where = ctx.query;
        where.applyUser = where.applyUser ? JSON.parse(where.applyUser) : undefined;
        where.approveUser = where.approveUser ? JSON.parse(where.approveUser) : undefined;
        where.createDate = where.createDate ? JSON.parse(where.createDate) : undefined;
        where.approveTime = where.approveTime ? JSON.parse(where.approveTime) : undefined;
        const r = await ctx.api.old.get('billing/spareMoney/application', {
            where,
            skip: 0,
            count: 1,
            limit: 1,
        });
        if (r.count) {
            ctx.body = {
                code: 0,
                data: r.count,
            };
        } else {
            ctx.throwCode(1, '没有符合条件的数据');
        }
    }

    async exportSpareMoneyTable() {
        const { ctx } = this;
        const StatusMap = {
            1: '未审核',
            2: '已审核',
            3: '已驳回',
        };

        const DetailTypeMap = {
            3: '报销',
            4: '撤回报销款',
            5: '备用金申领',
            6: '备用金调整',
        };

        const DetailStatusMap = {
            1: '未对账',
            2: '对账中',
            3: '已对账',
        };
        const header = ['机构名称', '申领人', '申领金额', '申领日期', '审批人', '审批日期', '申领单状态', '交易时间', '记账日期', '类型', '出', '流水状态', '包裹编号', '患者姓名', '万户卡号'];
        const PAGE_SIZE = 100;
        let skip = 0;
        const total = Number(ctx.query.limit) || Number.MAX_SAFE_INTEGER;
        const {
            applyUser, approveUser, createDate, approveTime, status, hospitalAccountId,
        } = ctx.query;
        const where = {
            applyUser: applyUser ? JSON.parse(applyUser) : undefined,
            approveUser: approveUser ? JSON.parse(approveUser) : undefined,
            createDate: createDate ? JSON.parse(createDate) : undefined,
            approveTime: approveTime ? JSON.parse(approveTime) : undefined,
            status,
            hospitalAccountId,
        };
        const output = xlsx();
        const filename = '备用金申领表.xlsx';
        this.ctx.response.attachment(filename);
        this.ctx.body = output;

        (async () => {
            while (skip < total) {
                const r = await this.ctx.api.old.get('billing/spareMoney/application', {
                    where,
                    count: 0,
                    skip,
                    limit: PAGE_SIZE,
                });
                output.write(header);
                if (r.list) {
                    for (let i = 0; i < r.list.length; i += 1) {
                        const app = r.list[i];
                        let askip = 0;
                        /* eslint-disable no-constant-condition */
                        while (true) {
                            const ar = await this.ctx.api.old.get('billing/spareMoney/journal', {
                                where: {
                                    hospitalAccountId,
                                    depositId: app.id,
                                },
                                skip: askip,
                                limit: PAGE_SIZE,
                            });
                            for (const d of ar.list) {
                                const row = [
                                    app.doctorHospital,
                                    app.applyUser,
                                    centToYuan(app.applyMoney, 2),
                                    app.createDate,
                                    app.approveUser,
                                    app.approveTime,
                                    StatusMap[app.status],
                                    d.transactionDate,
                                    d.accountingDate,
                                    DetailTypeMap[d.type],
                                    centToYuan(d.balanceOut, 2),
                                    DetailStatusMap[d.status],
                                    d.orderfillNo,
                                    d.patientName,
                                    d.accountNo,
                                ];
                                await writeAndDrain(output, row);
                            }
                            if (ar.list.length < PAGE_SIZE) {
                                break;
                            }
                            askip += PAGE_SIZE;
                        }
                    }
                    if (r.list.length < PAGE_SIZE) {
                        break;
                    }
                }
                skip += PAGE_SIZE;
            }
        })().then(() => {
            output.finalize();
            output.end();
        }).catch((e) => {
            output.finalize();
            output.end();
            this.logger.error('exportSpareMoneyTable background task error', e);
        });
    }

    async exportReservationTable() {
        const { ctx } = this;
        const whereString = ctx.query.where;
        const StatusMap = {
            1: '已预约',
            2: '已确认',
            3: '已取消',
        };
        const SexMap = {
            0: '女',
            1: '男',
        };
        const Headers = ['会员', '性别', '年龄', '手机号码', '其他联系方式号',
            '现有疾病', '预约医生', '预约时间', '预约机构', '预约备注',
            '预约状态', '创建人', '更新人', '创建时间', '更新时间'];
        const PAGE_SIZE = 100;
        let skip = 0;
        const output = xlsx();
        const filename = '会员预约表.xlsx';
        ctx.response.attachment(filename);
        ctx.body = output;

        (async () => {
            /* 不知道为什么，立即写入输出流会导致Excel文件无法打开，异步写就没问题，所以此处等待一个timeout */
            await new Promise(fulfill => setTimeout(fulfill));
            await writeAndDrain(output, Headers);
            while (true) {
                const list = await ctx.api.portal.get('patients/reservationRecordExport', {
                    where: whereString,
                    skip,
                    limit: PAGE_SIZE,
                });
                if (!list || !list.length) break;
                for (const item of list) {
                    const row = [
                        item.patientName,
                        SexMap[item.sex],
                        item.age,
                        item.phone,
                        item.machineNumber,
                        item.disease && item.disease.join('、'),
                        item.doctorName,
                        item.appointmentTimeStr,
                        item.hospitalName,
                        item.remarks,
                        StatusMap[item.status],
                        `${item.createByName}（${item.createByCompany}${item.createByDepartment ? `，${item.createByDepartment}` : ''}）`,
                        `${item.updateByName}（${item.updateByCompany}${item.updateByDepartment ? `，${item.updateByDepartment}` : ''}）`,
                        item.createDate && moment(item.createDate).format('YYYY-MM-DD HH:mm:ss'),
                        item.updateDate && moment(item.updateDate).format('YYYY-MM-DD HH:mm:ss'),
                    ];
                    await writeAndDrain(output, row);
                }
                skip += list.length;
            }
        })().then(() => {
            this.logger.info('exportReservationTable success with', skip, 'items');
            output.finalize();
            output.end();
        }).catch((e) => {
            this.logger.error('exportReservationTable background task error', e);
            output.write(['导出过程中出现了错误，以上数据可能不全']);
            output.finalize();
            output.end();
        });
    }

    /* 收入管理列表导出 */
    async exportIncomeTable() {
        function countDifferentOrder(list) {
            let c = 0;
            let lastId;
            for (const item of list) {
                if (!item.orderfillNo) {
                    c += 1;
                    lastId = null;
                } else {
                    const id = `${item.orderfillNo}-${item.traAdjust}`;
                    if (lastId !== id) {
                        lastId = id;
                        c += 1;
                    }
                }
            }
            return c;
        }
        const { ctx } = this;
        const whereString = ctx.query;
        const Headers = ['包裹单编号', '会员', '客户编号', '签约机构', '通用名（商品名）', '规格', '分类', '数量', '单价', '小计金额', '金额', '报销金额', '实收金额', '对账月份', '记账日期'];
        const PAGE_SIZE = 1000;// 收入明细查询api的单次开销比较大，所以导出的时候查询次数太多会影响整体效率
        let skip = 0;
        const output = xlsx();
        const filename = '收入管理表.xlsx';
        ctx.response.attachment(filename);
        ctx.body = output;
        (async () => {
            await new Promise(fulfill => setTimeout(fulfill));
            await writeAndDrain(output, Headers);
            /* eslint-disable no-constant-condition */
            while (true) {
                const list = await ctx.api.portal.get('/billing/income/detail', {
                    ...whereString,
                    skip,
                    limit: PAGE_SIZE,
                });
                if (!list || !list.length) break;
                for (const item of list) {
                    const row = [
                        item.orderfillNo,
                        item.name,
                        item.patientId,
                        item.hospitalName,
                        item.drugName,
                        item.standard,
                        item.largeClass,
                        item.number,
                        item.price && centToYuan(item.price, 2),
                        item.subAmount && centToYuan(item.subAmount, 2),
                        item.amount && centToYuan(item.amount, 2),
                        item.refundAmount && centToYuan(item.refundAmount, 2),
                        item.realAmount && centToYuan(item.realAmount, 2),
                        item.statementDate && moment(item.statementDate).format('YYYYMM'),
                        item.accountDate && moment(item.accountDate).format('YYYY-MM-DD'),
                    ];
                    await writeAndDrain(output, row);
                }
                skip += countDifferentOrder(list);
            }
            const sum = await ctx.api.portal.get('/billing/income/detail/sum', whereString);
            const sumRow = [
                '合计',
                '',
                '',
                '',
                '',
                '',
                '',
                '',
                '',
                '',
                sum.amount && centToYuan(sum.amount, 2),
                sum.refundAmount && centToYuan(sum.refundAmount, 2),
                sum.realAmount && centToYuan(sum.realAmount, 2),
                '',
                '',
            ];
            await writeAndDrain(output, sumRow);
        })().then(() => {
            output.finalize();
            output.end();
        }).catch(() => {
            output.write(['导出过程中出现了错误，以上数据可能不全']);
            output.finalize();
            output.end();
        });
    }

    async exportInsuranceZip() {
        const { ctx } = this;
        const { insurId } = ctx.params;
        const groupInsurance = await ctx.api.portal.get(`team_insurance/${insurId}`);
        const orderList = await ctx.api.portal.get(`team_insurance/${insurId}/order`);
        const insurancePath = `${this.config.insurance.path}/${groupInsurance.orderNo}`
        deleteall(insurancePath)
        fs.mkdirSync(insurancePath);
        //生成excel
        const excelPath = `${insurancePath}/${groupInsurance.orderNo}.xlsx`;
        const options = {
            filename: excelPath,
            useStyles: true,
        };
        const workbook = new Excel.stream.xlsx.WorkbookWriter(options);
        const worksheet = workbook.addWorksheet('Sheet1', { properties: { defaultRowHeight: 25 } });
        createTitleForInsurance(worksheet, groupInsurance);
        worksheet.getColumn(7).width = 15;
        worksheet.getColumn(9).width = 20;
        orderList ? orderList.map((item, index) => {
            var order = item.insuredIdCard.slice(-3);
            const sex = (order % 2 === 0 ? '女' : '男');
            const value = [
                1 + index * 1,
                '在职',
                item.insuredName,
                item.insuredName,
                '本人',
                sex,
                item.insuredBirthday,
                '身份证',
                item.insuredIdCard,
                item.code,
                '一类',
                '0001001',
                '城镇职工',
                '法定',

            ];
            worksheet.addRow(value);
        }) : null
        await workbook.commit();

        for (const order of orderList) {
            const filePath = `${insurancePath}/${order.insuredName || ''}_${order.insuredIdCard || ''}`
            fs.mkdirSync(filePath);
            if (order.pics) {
                for (const index in order.pics) {
                    const orderPic = order.pics[index];
                    const exta = orderPic.path.substring(orderPic.path.lastIndexOf('.'), orderPic.path.length)
                    const fileName = orderPic.type === 1 ? `贫困证明_${index + 1}${exta}` : `授权确认书_${1 + index * 1}${exta}`;
                    const imgPath = orderPic.path;
                    const writeStream = fs.createWriteStream(`${filePath}/${fileName}`)
                    await writeFile(imgPath, writeStream)
                }
            }
        }
        await new Promise(fulfill => setTimeout(fulfill));
        await compressing.zip.compressDir(insurancePath, `${insurancePath}.zip`);
        deleteall(insurancePath);
        this.ctx.attachment(`${insurancePath}.zip`);
        this.ctx.set('Content-Type', 'application/octet-stream');
        this.ctx.body = fs.createReadStream(`${insurancePath}.zip`);
    }

};

function createTitleForInsurance(worksheet, groupInsurance) {
    worksheet.mergeCells('A1:R1');
    worksheet.mergeCells('A2:R2');
    worksheet.mergeCells('A3:R3');
    worksheet.mergeCells('A4:R4');
    const cellTitle = worksheet.getCell('A2');
    cellTitle.value = "团体保险被保险人清单";
    cellTitle.font = { name: '华文楷体', size: 9, bold: true };
    cellTitle.alignment = { vertical: 'middle', horizontal: 'center' };
    const cellNotice = worksheet.getCell('A3');
    cellNotice.value = '被保险人重要提示：若您同意参加本团体保险并同意昆仑健康保险股份有限公司在核保、保险单管理以及案件处理等涉及到您投保的此份团体业务过程中，使用您在此清单中填写的内容，请您在此清单的被保险人签名栏中亲笔签名。';
    cellNotice.font = { name: '宋体', size: 10, bold: true };
    cellNotice.alignment = { vertical: 'middle', horizontal: 'center' };
    const insuranceUnit = worksheet.getCell('A4');
    insuranceUnit.value = `投保单位：北京万户良方科技有限公司                   投保单号：${groupInsurance.orderNo}`;
    insuranceUnit.font = { name: '宋体', size: 9, bold: true };
    insuranceUnit.alignment = { vertical: 'middle', horizontal: 'center' };

    const title = ['序号', '状态*', '员工姓名*', '被保险人姓名*', '与员工关系*', '性别*', '出生日期*', '证件类型*', '证件号码*', '保障计划*', '职业类别*', '职业代码*', '医保标识*', '受益人*', '开户行全称', '户名', '账号', '被保险人签名'];
    const row = worksheet.getRow(5);
    for (const textIndex in title) {
        let index = textIndex;
        index++;
        worksheet.mergeCells(5, index, 6, index); // 上，左，下，右
        const cell = row.getCell(index);
        cell.value = title[textIndex];
        cell.alignment = { vertical: 'middle', horizontal: 'center' };
        if (title[textIndex].indexOf('*') >= 0) {
            cell.font = { name: '宋体', size: 9, bold: true, color: { 'argb': 'FF2121' } };
        } else {
            cell.font = { name: '宋体', size: 9, bold: true };
        }
    }
}
